libobs_wrapper\data\properties/
mod.rs

1//! This module is used to handle properties in OBS.
2//! Properties essentially tell you what settings for example a output accepts, by using the ´get_properties´ method
3//! on the according struct.
4//! This lets you get all properties at runtime, rather than having to rely on preset values, which
5//! can be useful if you want to let the user adust specific system dependent settings on for example an encoder.
6
7mod enums;
8mod macros;
9pub mod prop_impl;
10pub mod types;
11
12use std::{collections::HashMap, ffi::CStr};
13
14use libobs::obs_properties;
15use macros::*;
16
17pub use enums::*;
18use types::*;
19
20use crate::{
21    impl_obs_drop, run_with_obs,
22    runtime::ObsRuntime,
23    unsafe_send::{Sendable, SmartPointerSendable},
24    utils::{ObsDropGuard, ObsError, ObsString},
25};
26
27#[derive(Debug)]
28pub(crate) struct _ObsPropertiesDropGuard {
29    properties: Sendable<*mut obs_properties>,
30    runtime: ObsRuntime,
31}
32
33impl ObsDropGuard for _ObsPropertiesDropGuard {}
34
35impl_obs_drop!(_ObsPropertiesDropGuard, (properties), move || unsafe {
36    // Safety: The pointer is valid as long as we are in the runtime and the guard is alive.
37    libobs::obs_properties_destroy(properties.0);
38});
39
40impl _ObsPropertiesDropGuard {
41    pub(crate) fn new(properties: Sendable<*mut obs_properties>, runtime: ObsRuntime) -> Self {
42        Self {
43            properties,
44            runtime,
45        }
46    }
47}
48
49#[derive(Debug, Clone)]
50pub enum ObsProperty {
51    /// A property that is not valid
52    Invalid,
53    /// A boolean property
54    Bool,
55    /// An integer property
56    Int(ObsNumberProperty<i32>),
57    /// A float property
58    Float(ObsNumberProperty<f64>),
59    /// A text property
60    Text(ObsTextProperty),
61    /// A path property
62    Path(ObsPathProperty),
63    /// A list property
64    List(ObsListProperty),
65    /// A color property
66    Color(ObsColorProperty),
67    /// A button property
68    Button(ObsButtonProperty),
69    /// A font property
70    Font(ObsFontProperty),
71    /// An editable list property
72    EditableList(ObsEditableListProperty),
73    /// A frame rate property
74    FrameRate(ObsFrameRateProperty),
75    /// A group property
76    Group(ObsGroupProperty),
77    /// A color alpha property
78    ColorAlpha(ObsColorAlphaProperty),
79}
80
81pub trait ObsPropertyObjectPrivate {
82    fn get_properties_raw(
83        &self,
84    ) -> Result<SmartPointerSendable<*mut libobs::obs_properties_t>, ObsError>;
85    fn get_properties_by_id_raw<T: Into<ObsString> + Sync + Send>(
86        id: T,
87        runtime: ObsRuntime,
88    ) -> Result<SmartPointerSendable<*mut libobs::obs_properties_t>, ObsError>;
89}
90
91pub(crate) fn property_ptr_to_struct(
92    properties_raw: SmartPointerSendable<*mut obs_properties>,
93    runtime: ObsRuntime,
94) -> Result<HashMap<String, ObsProperty>, ObsError> {
95    let runtime_clone = runtime.clone();
96    run_with_obs!(runtime, (properties_raw, runtime_clone), move || {
97        let mut result = HashMap::new();
98        let mut property = unsafe {
99            // Safety: Safe because of smart pointer
100            libobs::obs_properties_first(properties_raw.get_ptr())
101        };
102        while !property.is_null() {
103            let name = unsafe { libobs::obs_property_name(property) };
104            if name.is_null() {
105                let success = unsafe {
106                    // Safety: Safe because property is not null and we are just moving forward.
107                    libobs::obs_property_next(&mut property)
108                };
109
110                if !success {
111                    break;
112                }
113                continue;
114            }
115
116            let name = unsafe {
117                // Safety: Safe because of we did a null check
118                CStr::from_ptr(name as _)
119            };
120            let name = name.to_string_lossy().to_string();
121
122            let p_type = unsafe {
123                // Safety: Safe because we just got the property pointer
124                libobs::obs_property_get_type(property)
125            };
126
127            let p_type = crate::macros::enum_from_number!(ObsPropertyType, p_type);
128
129            log::trace!("Property: {:?}", name);
130            match p_type {
131                Some(p_type) => {
132                    let prop_struct = unsafe {
133                        // Safety: Safe because we just got the property pointer
134                        p_type.get_property_struct(&runtime_clone, Sendable(property))
135                    };
136                    if let Ok(r) = prop_struct {
137                        result.insert(name, r);
138                    }
139                }
140                None => {
141                    result.insert(name, ObsProperty::Invalid);
142                }
143            }
144
145            // Move to the next property
146            let has_next = unsafe {
147                // Safety: We didn't drop the property, so it is still valid and we can proceed
148                libobs::obs_property_next(&mut property)
149            };
150
151            if !has_next {
152                break;
153            }
154        }
155
156        result
157    })
158}
159
160/// This trait is implemented for all obs objects that can have properties
161pub trait ObsPropertyObject: ObsPropertyObjectPrivate {
162    /// Returns the properties of the object
163    fn get_properties(&self) -> Result<HashMap<String, ObsProperty>, ObsError>;
164    fn get_properties_by_source_id<T: Into<ObsString> + Sync + Send>(
165        id: T,
166        runtime: &ObsRuntime,
167    ) -> Result<HashMap<String, ObsProperty>, ObsError> {
168        let properties_raw = Self::get_properties_by_id_raw(id, runtime.clone())?;
169        property_ptr_to_struct(properties_raw, runtime.clone())
170    }
171}